/**
 * FileName: DynamicUtil
 * Author:   SAMSUNG-PC 孙中军
 * Date:     2018/12/21 14:40
 * Description:动态类
 */
package cn.com.bonc.dynamic;


import javassist.*;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;

public class DynamicUtil implements Serializable{

    /** properties文件的资源路径*/
    private static final String RESOURCE_PATH = "dynamic-bean2.properties";

    /** Kafa数据分割符*/
    private static final String REGEX_STRING = "[|]";

    /** 动态类的名称*/
    //private static final String CLASS_NAME = "cn.com.bonc.domain.DynamicBean";
    private static final String CLASS_NAME = "DynamicBean";

    /** 保证与properties文件key顺序相同的keyList*/
    private List<String> propertyKeyList;

    /** 从文件读取出的properties*/
    private Properties properties;

    /** 生成的动态类*/
    private Class dynamicClazz;


    /**
     * 获取动态类方法
     * @return
     * @throws NotFoundException
     * @throws CannotCompileException
     * @throws IOException
     */
    public Class getDynamicClazz() throws NotFoundException, CannotCompileException, IOException {
        if (dynamicClazz==null){
            initDynamicClass();
        }
        return dynamicClazz;
    }

    public DynamicUtil() throws NotFoundException, CannotCompileException, IOException {

        initDynamicClass();
        System.out.println("=str==============================>initDynamicClass();");

    }

    /**
     * 从Jar中加载Properties
     * 当然也可以从其他地方加载数据来生成动态类，但必须保证以下两点
     * 1.必须是key-value的形式
     * 2.key的排列是有序的
     * @throws IOException
     */
    private void loadDynamicClassProperties() throws IOException {
        InputStream inputStream = DynamicUtil.class.getClassLoader().getResourceAsStream(RESOURCE_PATH);
        Properties properties = new OrderedProperties();
        properties.load(inputStream);
        propertyKeyList =new ArrayList();
        properties.keySet().stream().forEach( x->propertyKeyList.add(x.toString()));
        this.properties=properties;

    }

    /**
     * 采用读取 --files /path... /xxx.properties 方式
     * @throws IOException
     */
    private void loadExternalDynamicClassProperties() throws IOException {
        Properties properties = new OrderedProperties();
        properties.load(new FileInputStream(RESOURCE_PATH));
        propertyKeyList =new ArrayList();
        properties.keySet().stream().forEach( x->propertyKeyList.add(x.toString()));
        this.properties=properties;
    }
    /**
     * 读取一定格式数据，进行分割
     * 并将分割出来的值封装到动态类的对象中
     * @param str 一定格式的字符串数据
     * @return
     * @throws IOException
     * @throws CannotCompileException
     * @throws NotFoundException
     * @throws IllegalAccessException
     * @throws InstantiationException
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws ClassNotFoundException
     */
    public Object getDynamicObject(String str) throws IOException, CannotCompileException, NotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, ClassNotFoundException {
        List<String> valueList = Arrays.asList(str.split(REGEX_STRING));

        Object dynamicObject = getDynamicClazz().newInstance();
        int index=0;
        for (String prop:propertyKeyList){
            Class<?> typeClass = Class.forName(properties.getProperty(prop));
            Method setter = dynamicObject.getClass().getMethod("set"+ upperFirst(prop), typeClass);
            setter.invoke(dynamicObject, valueList.get(index++));
        }
        return dynamicObject;
    }


    /**
     * 将字符串中的第一个字符变为大写，并返回这个字符串
     * @param toUpper 待转换的字符串
     * @return
     */
    private String upperFirst(String toUpper){
        return toUpper.substring(0, 1).toUpperCase()+ toUpper.substring(1);
    }

    /**
     * 动态类的初始化，主要通过Javaassist来实现此功能
     * 将properties转换为一个具体的类
     * @throws IOException
     * @throws CannotCompileException
     * @throws NotFoundException
     */
    private void initDynamicClass() throws IOException, CannotCompileException, NotFoundException {
        ClassPool pool = ClassPool.getDefault();
        CtClass ctClass =pool.makeClass(CLASS_NAME);

        //loadDynamicClassProperties();
        loadExternalDynamicClassProperties();


        for (String prop:propertyKeyList){

//            CtField ctField = CtField.make("private String "+prop+";", ctClass);
//            ctClass.addField(ctField);

            CtField ctField = new CtField(pool.get("java.lang.String"),prop, ctClass);
            ctClass.addField(ctField);

            String propertyType =properties.getProperty(prop);

            String getMethodString ="public "+propertyType+" get"+upperFirst(prop)+"() { return this."+prop+";}";
            String setMethodString ="public void set"+upperFirst(prop)+"("+propertyType+" "+prop+") { this."+prop+" = "+prop+";}";
            CtMethod getMethod = CtNewMethod.make(getMethodString, ctClass);
            CtMethod setMethod = CtNewMethod.make(setMethodString, ctClass);
            ctClass.addMethod(getMethod);
            ctClass.addMethod(setMethod);
        }
        this.dynamicClazz= ctClass.toClass();
    }


    /**
     * 对数据kafka格式进行过滤
     * 目前使用的数据格式为：
     * 漆雕滢|371523191105011680|山东济宁市金乡县王丕镇|18166541879|wowta63765@msn.com|6224228625456113315
     * Property定义的 属性个数与数据以“|”分割后的数据相同
     * @param str kafka数据
     * @return
     * @throws IOException
     * @throws CannotCompileException
     * @throws NotFoundException
     */
    public boolean isStandardData(String str) throws IOException, CannotCompileException, NotFoundException {
        getDynamicClazz();
        List<String> valueList = Arrays.asList(str.split(REGEX_STRING));
        return propertyKeyList.size()==valueList.size()?true:false;

    }
}
